package com.alcohol.auth.server.support.core;

import com.alcohol.auth.server.service.SysUserService;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.util.Objects;

/**
 * 表单登录处理器
 * @author LiXinYu
 * @date 2025/11/11
 */
@Slf4j
@AllArgsConstructor
public class FormLoginAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {

    private SysUserService sysUserService;
    private PasswordEncoder passwordEncoder;

    /**
     * 校验密码有效性
     * @param userDetails 用户详细信息
     * @param authentication 身份验证令牌
     * @throws AuthenticationException 身份验证失败时抛出异常
     */
    @Override
    protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
        if (Objects.isNull(authentication.getCredentials())) {
            log.debug("Failed to authenticate since no credentials provided");
            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        }
        String presentedPassword = authentication.getCredentials().toString();
        if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
            log.debug("Failed to authenticate since password does not match stored value");
            throw new BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
        }
    }

    /**
     * 根据用户名检索用户详情
     * @param username 用户名
     * @param authentication 认证令牌
     * @return 用户详情信息
     * @throws InternalAuthenticationServiceException
     * 当无法获取请求、未注册UserDetailsService或加载用户失败时抛出
     * @throws UsernameNotFoundException 当用户名不存在时抛出
     */
    @Override
    @SneakyThrows
    protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) {
        UserDetails loadedUser = sysUserService.loadUserByUsername(username);
        if (Objects.isNull(loadedUser)) {
            throw new InternalAuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation");
        }
        return loadedUser;
    }

    /**
     * 创建认证成功后的Authentication对象
     * @param principal 认证主体
     * @param authentication 认证信息
     * @param user 用户详情
     * @return 认证成功后的Authentication对象
     */
    @Override
    protected Authentication createSuccessAuthentication(Object principal, Authentication authentication, UserDetails user) {
        return super.createSuccessAuthentication(principal, authentication, user);
    }
}
